<template>
  <div class="monaco-editor-vue3" :style="style"></div>
</template>

<script>

import * as monaco from 'monaco-editor'

export default {
  name: 'vueMonacoEditor',
  props: {
    diffEditor: {type: Boolean, default: false},
    width: {type: [String, Number], default: '100%'},
    height: {type: [String, Number], default: '100%'},
    original: String,
    modelValue: String,
    language: {type: String, default: 'javascript'},
    theme: {type: String, default: 'vs'},
    options: {
      type: Object,
      default() {
        return {}
      },
    },
  },
  watch: {
    options: {
      deep: true,
      handler(options) {
        this.editor.updateOptions(options)
      },
    },
    value() {
      this.modelValue !== this._getValue() && this._setValue(this.modelValue)
    },
    original() {
      this._setOriginal()
    },
    language() {
      if (!this.editor) return
      if (this.diffEditor) {
        const {original, modified} = this.editor.getModel()
        monaco.editor.setModelLanguage(original, this.language)
        monaco.editor.setModelLanguage(modified, this.language)
      } else
        monaco.editor.setModelLanguage(this.editor.getModel(), this.language)
    },
    theme() {
      monaco.editor.setTheme(this.theme)
    },
  },
  computed: {
    style() {
      const height = this.height
      const width = this.width
      const fixedWidth = width.toString().includes('%')
          ? width
          : `${width}px`
      const fixedHeight = height.toString().includes('%')
          ? height
          : `${height}px`
      return {
        width: fixedWidth,
        height: fixedHeight,
        'text-align': 'left',
      }
    }
  },
  created() {

  },
  mounted() {
    this.initMonaco()
  },
  beforeUnmount() {
    this.editor && this.editor.dispose()
  },
  methods: {
    initMonaco() {
      this.$emit('editorWillMount', monaco)
      const {modelValue, language, theme, options} = this
      this.editor = monaco.editor[
          this.diffEditor ? 'createDiffEditor' : 'create'
          ](this.$el, {
        value: modelValue,
        language: language,
        theme: theme,
        ...options,
      })
      this.diffEditor && this._setModel(this.modelValue, this.original)

      // @event `change`
      const editor = this._getEditor()
      editor &&
      editor.onDidChangeModelContent((event) => {
        const value = editor.getValue()
        if (this.modelValue !== value) {
          this.$emit('change:modelValue', value, event)
          this.$emit('update:modelValue', value)
        }
      })

      this.$emit('editorDidMount', this.editor)
    },
    _setModel(value, original) {
      const {language} = this
      const originalModel = monaco.editor.createModel(original, language)
      const modifiedModel = monaco.editor.createModel(value, language)
      this.editor.setModel({
        original: originalModel,
        modified: modifiedModel,
      })
    },
    _setValue(value) {
      let editor = this._getEditor()
      if (editor) return editor.setValue(value)
    },
    _getValue() {
      let editor = this._getEditor()
      if (!editor) return ''
      return editor.getValue()
    },
    _getEditor() {
      if (!this.editor) return null
      return this.diffEditor ? this.editor.modifiedEditor : this.editor
    },
    _setOriginal() {
      const {original} = this.editor.getModel()
      original.setValue(this.original)
    },
  },

}
</script>
